package com.cj.peiwan.server.dao;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.wei.db.mongo.MongoDbHelper;
import org.wei.db.mongo.MongoDbHelperFactory;
import org.wei.db.mongo.Sequence;
import org.wei.utils.CollectionUtil;
import org.wei.utils.exception.NullResultExcepion;

import com.cj.peiwan.entity.AEntity;
import com.cj.peiwan.server.exception.NotSpecifyIdException;
import com.cj.peiwan.server.other.IFiler;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;

public abstract class ABaseDao {
    public static boolean isTest = true;
    private static Map<String, String> collectionNameCacheMap = new HashMap<String, String>();
    protected static MongoDbHelper mongoDbHelper;
    public static final String COLLE_SEQUENCES = "sequences";

    public <T extends AEntity> T findBy(Long id) throws NullResultExcepion {
        List<T> list = findBy(new BasicDBObject(AEntity.ATTR_ID, id), 0, 1);
        if (list.size() == 0) {
            throw new NullResultExcepion("id:" + id);
        }
        return list.get(0);
    }

    public <T extends AEntity> List<T> findAll(int offset, int limit) {
        return findBy(new BasicDBObject(), offset, limit);
    }

    public <T extends AEntity> List<T> findWhere(String expression,
                                                 Map<String, Object> sort, int offset, int limit)
            throws NullResultExcepion {
        return getMongoDbHelper().findWhere(getCollectionName(), expression,
                new BasicDBObject(sort), offset, limit, getT());
    }

    public Long getLastId() {
        return getMongoDbHelper().getLastId(getCollectionName());
    }

    public <T extends AEntity> List<T> findBy(Map<String, Object> filter,
                                              int offset, int limit) {
        if (filter == null) {
            filter = new HashMap<String, Object>();
        }
        List<T> list = findBy(new BasicDBObject(filter), new BasicDBObject(),
                offset, limit);
        return list;
    }

    public <T extends AEntity> List<T> findBy(Map<String, Object> filter,
                                              Map<String, Object> sort, int offset, int limit) {
        if (filter == null) {
            filter = new HashMap<String, Object>();
        }
        if (sort == null) {
            sort = new HashMap<String, Object>();
        }
        List<T> list = getMongoDbHelper().findList(getCollectionName(),
                new BasicDBObject(filter), new BasicDBObject(sort), offset,
                limit, getT());
        if (list == null) {
            list = new ArrayList<T>();
        }
        return list;
    }

    public <T extends AEntity> List<T> findBy(Map<String, Object> filter,
                                              IFiler<T> filer2, Comparator<T> comparator, int offset, int limit) {
        if (filter == null) {
            filter = new HashMap<String, Object>();
        }
        int tmpOffset = 0;
        int tmpLimit = 2000;
        List<T> tmpList = findBy(filter, tmpOffset, tmpLimit);
        if (tmpList == null) {
            tmpList = new ArrayList<T>();
        }
        if (tmpList.isEmpty()) {
            return tmpList;
        }
        List<T> result = new ArrayList<T>();
        while (!tmpList.isEmpty()) {
            Iterator<T> it = tmpList.iterator();
            while (it.hasNext()) {
                T t = it.next();
                if (!filer2.isRetain(t)) {
                    it.remove();
                    //continue;
                }
            }
            CollectionUtil<T> collectionUtil = new CollectionUtil<T>();
            if (comparator != null) {
                result = collectionUtil.addToFixSize(result, tmpList, offset
                        + limit, comparator);
            } else {
                result = collectionUtil.addToFixSize(result, tmpList, offset
                        + limit);
            }
            tmpOffset += tmpLimit;
            tmpList = findBy(filter, tmpOffset, tmpLimit);
        }
        int l = offset + limit;
        if (result.size() <= offset) {
            return new ArrayList<T>();
        }
        if (result.size() < offset + limit) {
            l = result.size();
        }
        result = result.subList(offset, l);
        return result;
    }

    public void deleteBy(Long id) {
        getMongoDbHelper().remove(getCollectionName(),
                new BasicDBObject(AEntity.ATTR_ID, id));
    }

    public void deleteBy(Map<String, Object> filter) {
        getMongoDbHelper().remove(getCollectionName(),
                new BasicDBObject(filter));
    }

    public void deleteAll() {
        getMongoDbHelper().remove(getCollectionName(), new BasicDBObject());
    }

    public void update(Map<String, Object> query, Map<String, Object> operation) {
        update(query, operation, MongoDbHelper.MODIFIER_SET, false, true);
    }

    public void updateBy(Long id, Map<String, Object> operation) {
        update(new BasicDBObject(AEntity.ATTR_ID, id), new BasicDBObject(
                operation));
    }

    public void update(Map<String, Object> query,
                       Map<String, Object> operation, String modifier, boolean isUpsert,
                       boolean isMuilt) {
        getMongoDbHelper().update(getCollectionName(),
                new BasicDBObject(query), new BasicDBObject(operation),
                modifier, isUpsert, isMuilt);
    }

    public void upsert(Map<String, Object> query,
                       Map<String, Object> operation) {
        update(query, operation, MongoDbHelper.MODIFIER_SET, true, false);
    }

    public <T extends AEntity> T updateBy(Long id,
                                          Map<String, Object> operation, String modifier)
            throws NullResultExcepion {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put(AEntity.ATTR_ID, id);
        update(map, operation, modifier, false, false);
        return findBy(id);
    }

    public <T extends AEntity> void insert(T t) throws NotSpecifyIdException {
        if (t.getId() == null) {
            throw new NotSpecifyIdException();
        }
        getMongoDbHelper().insert(getCollectionName(), t);
    }

    public int getCount(Map<String, Object> query) {
        return getMongoDbHelper().getCount(getCollectionName(),
                BasicDBObjectBuilder.start(query).get());
    }

    public long getNextId() {
        Long id = getMongoDbHelper().getNextId(getCollectionName());
        if (id == null) {
            if (isTest) {
                id = 1L;
            } else {
                id = 100000L;
            }
            Sequence sequence = new Sequence();
            sequence.setName(getCollectionName());
            sequence.setValue(id);
            getMongoDbHelper().insert(COLLE_SEQUENCES, sequence);

        }
        return id;
    }

    protected String getCollectionName() {
        String simpleName = getT().getSimpleName();
        String r = collectionNameCacheMap.get(simpleName);
        if (r == null) {
            char[] chars = simpleName.toCharArray();
            chars[0] = Character.toLowerCase(chars[0]);
            r = new String(chars) + "s";
            collectionNameCacheMap.put(simpleName, r);
        }
        return r;
    }

    protected abstract Class<?> getT();

    public synchronized static MongoDbHelper getMongoDbHelper() {
        if (mongoDbHelper == null) {
            mongoDbHelper = MongoDbHelperFactory.getMongoDbHelper();
        }
        return mongoDbHelper;
    }

}
